package org.tangerinejunction.imstats.ui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.text.AttributedString;
import java.util.Calendar;

import javax.swing.AbstractAction;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableModel;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
//import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.PieSectionLabelGenerator;
import org.jfree.chart.plot.PiePlot;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.data.general.PieDataset;
import org.jfree.util.SortOrder;
import org.tangerinejunction.imstats.data.Account;
import org.tangerinejunction.imstats.data.Entity;
import org.tangerinejunction.imstats.data.IMDataset;
import org.tangerinejunction.imstats.data.TableFactory;
import org.tangerinejunction.imstats.util.Constants;
import org.tangerinejunction.imstats.util.Increment;
import org.tangerinejunction.imstats.util.TableSorter;
import org.tangerinejunction.imstats.util.TimeRange;

import prefuse.data.Table;
import prefuse.data.Tuple;
import prefuse.data.util.TableIterator;

/**
 * This is part of the new UI.  The SummaryFrame shows a summary
 * of statistics pertaining to the selected time range.  Right
 * now it's a pie chart.
 * 
 * @author meganthorsen
 */
public class CorrespondentStatsFrame extends JFrame implements ActionListener, ComponentListener, MouseListener
{
	/**
	 * 
	 */
	private static final long serialVersionUID = -7584028864804189593L;
	
	IMDataset dataset;
	Table entityTable;
	Table accountTable;
	String viewType;
	Calendar start;
	Calendar end;
	JTable table;
 	JPopupMenu tableContextMenu;
	//ChartPanel chartPanel;
	//JComboBox fieldSelector;
	Preferences preferences;
	
	/**
	 * 
	 * @param dataset	the dataset to analyze
	 * @param dataTable	the data table to analyze
	 */
	public CorrespondentStatsFrame(IMDataset dataset, Preferences preferences, String viewType, Calendar start, Calendar end)
	{
		this.dataset = dataset;
		this.entityTable = dataset.getEntityTable();
		this.accountTable = dataset.getAccountTable();
		this.viewType = viewType;
		this.start = start;
		this.end = end;
		this.preferences = preferences;
		addComponentListener(this);
		
		createComponents();
		layoutComponents();
		setupEvents();
		setPreferredSize(new Dimension(700, 300));
		setTitle("Stats");
	}
	
	private void createComponents()
	{
		createTable();
 		
 		tableContextMenu = new JPopupMenu();
 		JMenuItem hideItem = new JMenuItem("Hide selection in chart");
 		hideItem.setAction(new AbstractAction("Hide selection in chart")
 		{
			public void actionPerformed(ActionEvent e) 
			{
				int[] selection = table.getSelectedRows();
				for (int row : selection)
				{
					TableSorter sorter = (TableSorter)table.getModel();
					Tuple t = ((DataTableModel)sorter.getTableModel()).getTuple(sorter.modelIndex(row));
					t.set(Account.FILTERED, true);
				}
				((DataTableModel)((TableSorter)table.getModel()).getTableModel()).fireTableDataChanged();
			}
 		});
 		JMenuItem showItem = new JMenuItem("Show selection in chart");
 		showItem.setAction(new AbstractAction("Show selection in chart")
 		{
			public void actionPerformed(ActionEvent e) 
			{
				int[] selection = table.getSelectedRows();
				for (int row : selection)
				{
					TableSorter sorter = (TableSorter)table.getModel();
					Tuple t = ((DataTableModel)sorter.getTableModel()).getTuple(sorter.modelIndex(row));
					t.set(Account.FILTERED, false);
				}
				((DataTableModel)((TableSorter)table.getModel()).getTableModel()).fireTableDataChanged();
			}
 		});
 		tableContextMenu.add(hideItem);
 		tableContextMenu.add(showItem);
 		tableContextMenu.pack();
	}
	
	private void createTable()
	{
        if (accountTable.getColumn(TableFactory.NUM_DAYS_IN_DATASET_NAME) == null)
            accountTable.addColumn(TableFactory.NUM_DAYS_IN_DATASET_NAME, Double.class);
        if (entityTable.getColumn(TableFactory.NUM_DAYS_IN_DATASET_NAME) == null)
            entityTable.addColumn(TableFactory.NUM_DAYS_IN_DATASET_NAME, Double.class);
        if (accountTable.getColumn(TableFactory.CONVERSATIONS_PER_DAY_NAME) == null)
            accountTable.addColumn(TableFactory.CONVERSATIONS_PER_DAY_NAME, Double.class);
        if (entityTable.getColumn(TableFactory.CONVERSATIONS_PER_DAY_NAME) == null)
            entityTable.addColumn(TableFactory.CONVERSATIONS_PER_DAY_NAME, Double.class);
        
		DataTableModel model = new DataTableModel(getCurrentTable());
		table = new JTable(model);
		RankedTableSorter sorter = new RankedTableSorter(TableFactory.ROW_COL_NAME, model, table.getTableHeader());
		table.setModel(sorter);
		int convDayCol = getConversationsPerDayColumn();
		if (convDayCol > 0)
			sorter.setSortingStatus(convDayCol, TableSorter.DESCENDING);
		
		//chartViewer = new ChartViewer("Summary charts", dataTable, Application.TABLE_WINDOW_WIDTH, 0);
 		
 		table.addMouseListener(this);
	}
	
	private int getConversationsPerDayColumn()
	{
		for (int i = 0; i < table.getColumnCount(); i++)
		{
			if (table.getColumnName(i).equals(TableFactory.CONVERSATIONS_PER_DAY_NAME))
				return i;
		}
		return -1;
	}
	
	private void layoutComponents()
	{
		//JSplitPane content = new JSplitPane();
		//setContentPane(content);
		
		//GridBagLayout layout = new GridBagLayout();
		//setLayout(layout);
		//GridBagConstraints constraints = layout.getConstraints(this);		
		//constraints.gridx = 0;
		//constraints.gridy = 0;
		JPanel chartControlPanel = new JPanel();
		JLabel panelTitle = new JLabel("Stats");
		panelTitle.setFont(panelTitle.getFont().deriveFont(Font.BOLD));
		chartControlPanel.add(panelTitle);
		//chartControlPanel.add(fieldSelector);
		
		/*
		add(chartControlPanel, constraints);
		constraints.gridy++;
		constraints.weightx = 1;
		constraints.weighty = 1;
		constraints.fill = GridBagConstraints.BOTH;
		add(chartPanel, constraints);
		*/
 		
 		//JPanel upperPanel = new JPanel(new BorderLayout());
 		//upperPanel.add(chartControlPanel, BorderLayout.NORTH);
 		//upperPanel.add(chartPanel, BorderLayout.CENTER);
 		JPanel lowerPanel = new JPanel(new BorderLayout());
 		JScrollPane tablePane = new JScrollPane(table);
 		tablePane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
 		lowerPanel.add(tablePane, BorderLayout.CENTER);
 		add(lowerPanel);
 		//content.setTopComponent(upperPanel);
 		//content.setBottomComponent(lowerPanel);
 		//content.setOrientation(JSplitPane.VERTICAL_SPLIT);
 		//content.setOneTouchExpandable(true);
 		//content.setDividerLocation(1d);
	}
	
	private void setupEvents()
	{
		//fieldSelector.addActionListener(this);
	}
	
	/**
	 * Update the data table and chart.
	 * 
	 * @param dataTable	the data table
	 */
	public void updateDataTable(Table dataTable, Calendar start, Calendar end)
	{
        if (dataTable == getCurrentTable())
            ;
        else if (dataTable == dataset.getAccountTable())
            viewType = ControlPanel.ACCOUNT_VIEW;
        else
            viewType = ControlPanel.ENTITY_VIEW;
        this.start = start;
        this.end = end;
		RankedTableSorter sorter = (RankedTableSorter)table.getModel();
		((DataTableModel)sorter.getTableModel()).data = dataTable;
		//updateChart();
		updateTable();
	}
    
    /**
     * Update the data table and chart.
     * 
     * @param dataTable the data table
     */
    public void updateDataTable(Table dataTable)
    {
        if (dataTable == getCurrentTable())
            ;
        else if (dataTable == dataset.getAccountTable())
            viewType = ControlPanel.ACCOUNT_VIEW;
        else
            viewType = ControlPanel.ENTITY_VIEW;
        RankedTableSorter sorter = (RankedTableSorter)table.getModel();
        ((DataTableModel)sorter.getTableModel()).data = dataTable;
        //updateChart();
        updateTable();
    }
	
	/**
	 * Update the data table.
	 */
	public void updateTable()
	{
		//addColumns();
		RankedTableSorter sorter = (RankedTableSorter)table.getModel();
		sorter.fireTableDataChanged();
		sorter.fireTableStructureChanged();
		for (int i = 0; i < sorter.getColumnCount(); i++)
			if (sorter.getSortingStatus(i) != TableSorter.NOT_SORTED)
				sorter.setSortingStatus(i, sorter.getSortingStatus(i));
		table.updateUI();
		
		//chartViewer.updateDataTable(dataTable);
	}

	/**
	 * Update the chart.
	 */
	/*
	public void updateChart()
	{
		chartPanel.setChart(createChart());
		chartPanel.repaint();
	}
    */
	public void actionPerformed(ActionEvent e) 
	{
	    /*
        removeIncrementColumns();
        addIncrementColumns();
		if (e.getSource() == fieldSelector)
			updateChart();
			*/
	}
    
    /**
     * 
     * @return  the Accounts table if Account view is active; the Entity table if Entity view is active
     */
    public Table getCurrentTable()
    {
        return viewType.equals(ControlPanel.ACCOUNT_VIEW) ? accountTable : entityTable;
    }

    /**
     * Add increment columns to the table.
     */
    public void addIncrementColumns()
    {
        TableFactory.addColumnToTable(TableFactory.NUM_DAYS_IN_DATASET_NAME, entityTable, new TimeRange(start, end), true);
        TableFactory.addColumnToTable(TableFactory.NUM_DAYS_IN_DATASET_NAME, accountTable, new TimeRange(start, end), true);
        
        TableFactory.addColumnToTable(TableFactory.CONVERSATIONS_PER_DAY_NAME, entityTable, new TimeRange(start, end), true);
        TableFactory.addColumnToTable(TableFactory.CONVERSATIONS_PER_DAY_NAME, accountTable, new TimeRange(start, end), true);

        TableFactory.addColumnToTable(TableFactory.MAX_DAYS_BETWEEN_NAME, entityTable, new TimeRange(start, end), true);
        TableFactory.addColumnToTable(TableFactory.MAX_DAYS_BETWEEN_NAME, accountTable, new TimeRange(start, end), true);

        TableFactory.addColumnToTable(TableFactory.PROB_I_STARTED_NAME, entityTable, new TimeRange(start, end), true);
        TableFactory.addColumnToTable(TableFactory.PROB_I_STARTED_NAME, accountTable, new TimeRange(start, end), true);

        TableFactory.addColumnToTable(TableFactory.SIZE_I_STARTED_NAME, entityTable, new TimeRange(start, end), true);
        TableFactory.addColumnToTable(TableFactory.SIZE_I_STARTED_NAME, accountTable, new TimeRange(start, end), true);
        
        updateDataTable(getCurrentTable(), start, end);
    }
    
    /**
     * Remove increment columns from the table.
     */
    public void removeIncrementColumns()
    {
        int colCount = entityTable.getColumnCount();
        for (int i = colCount-1; i >= colCount; --i)
            entityTable.removeColumn(entityTable.getColumn(i));
        colCount = accountTable.getColumnCount();
        for (int i = colCount-1; i >= colCount; --i)
            accountTable.removeColumn(accountTable.getColumn(i));
    }
	
	/**
	 * The model for the data table display.
	 * 
	 * @author meganthorsen
	 */
	protected class DataTableModel extends AbstractTableModel
	{
		private static final long serialVersionUID = 132940759456976493L;

        private final String[] ignoreColumns = 
        {
            Account.FILTERED,
            Account.LOG_FILES_KEY,
            TableFactory.FILE_COUNT_COL_NAME,
            TableFactory.NORM_FILE_COUNT_COL_NAME,
            TableFactory.FILE_SIZE_COL_NAME,
            TableFactory.NORM_FILE_SIZE_COL_NAME,
            TableFactory.FILE_DENSITY_COL_NAME,
            TableFactory.NORM_FILE_DENSITY_COL_NAME,
            TableFactory.FREQUENT_LENGTH_COL_NAME
        };

		Table data;
		
		public DataTableModel(Table data)
		{
			this.data = data;
		}
		
		public int getColumnCount() 
		{
            int numCols = data.getColumnCount();
            for (String colName : ignoreColumns)
                if (data.getColumnNumber(colName) >= 0)
                    numCols--;
            return numCols;
		}

		public int getRowCount() 
		{
			return data.getRowCount();
		}
		
		private int getDataColumn(int col)
		{
            int tableCol = 0;
            int index = 0;
            while (index <= col && tableCol < data.getColumnCount())
            {
                final String colName = data.getColumnName(tableCol);
                int ignoreIndex = -1;
                for (int i = 0; i < ignoreColumns.length; i++)
                {
                    if (ignoreColumns[i].equals(colName))
                        ignoreIndex = i;
                }
                if (ignoreIndex < 0)
                    index++;
                tableCol++;
            }
            return tableCol-1;
		}

		public Object getValueAt(int row, int col) 
		{
			int column = getDataColumn(col);
			if (data.getColumnName(column).equals(Account.ENTITY_KEY))
				return ((Entity)data.get(row, column)).getName();
			else if (data.getColumnName(column).equals(Entity.ACCOUNTS_KEY))
			{
				if (CorrespondentStatsFrame.this.getCurrentTable().getTuple(row) instanceof Entity) 
				{
					Entity entity = (Entity)CorrespondentStatsFrame.this.getCurrentTable().getTuple(row);
					return entity.getAccounts().size();
				}
				else 
					return 1;
			}
			//if (data.getColumnName(col).equals(Account.LOG_FILES_KEY))
			//	return ((ArrayList)data.get(row, col)).size();
 			//if (data.getColumnName(getDataColumn(col)).equals(Account.FILTERED))
 			//	return (Boolean)getTuple(row).get(Account.FILTERED);
			return data.get(row, column);
		}
 		
 		public Tuple getTuple(int row)
 		{
 			return data.getTuple(row);
 		}
		
		public String getColumnName(int col)
		{
			return data.getColumnName(getDataColumn(col));
		}
		
		public Class<?> getColumnClass(int col)
		{
			int column = getDataColumn(col);
			if (data.getColumnName(column).equals(Account.ENTITY_KEY))
				return String.class;
			if (data.getColumnName(column).equals(Entity.ACCOUNTS_KEY))
				return Integer.class;
			//if (data.getColumnName(col).equals(Account.LOG_FILES_KEY))
			//	return Integer.class;
			return data.getColumnType(column);
		}
		
		public boolean isCellEditable(int row, int col)
		{
			if (data.getColumnName(getDataColumn(col)).equals(Account.ENTITY_KEY))
				return true;
			/*
 			else if (data.getColumnName(getDataColumn(col)).equals(Account.FILTERED))
 				return true;
 				*/
			else
				return false;
		}

		public void setValueAt(Object value, int row, int col)
		{
			if (data.getColumnName(getDataColumn(col)).equals(Account.ENTITY_KEY))
			{
				Account account = (Account)data.getTuple(row);
				dataset.addAccountToEntityByName((String)value, account);
			}
			/*
 			else if (data.getColumnName(getDataColumn(col)).equals(Account.FILTERED))
 			{
 				Tuple t = getTuple(row);
 				t.set(Account.FILTERED, value);
 			}
 			*/
		}
	}
	
	protected class RankedTableSorter extends TableSorter
	{
		private static final long serialVersionUID = 4963425141701216502L;
		
		private String rankColName;
		
		public RankedTableSorter(String rankColName)
		{
			super();
			this.rankColName = rankColName;
		}
		
		public RankedTableSorter(String rankColName, TableModel model)
		{
			super(model);
			this.rankColName = rankColName;
		}
		
		public RankedTableSorter(String rankColName, TableModel model, JTableHeader header)
		{
			super(model, header);
			this.rankColName = rankColName;
		}

	    public Object getValueAt(int row, int column) 
	    {
	    	if (tableModel.getColumnName(column).equals(rankColName))
	    		return row+1;
	        return tableModel.getValueAt(modelIndex(row), column);
	    }
	}

	public void componentHidden(ComponentEvent e) {}

	public void componentMoved(ComponentEvent e) {}

	public void componentResized(ComponentEvent e)
	{
		//((JSplitPane)getContentPane()).setDividerLocation(1d);
	}

	public void componentShown(ComponentEvent e) {}

	public void mouseClicked(MouseEvent e) {}

	public void mouseEntered(MouseEvent e) {}

	public void mouseExited(MouseEvent e) {}

	public void mousePressed(MouseEvent e) 
	{
		tableContextMenu.setVisible(false);
		if (e.isPopupTrigger())
		{
			JSplitPane pane = (JSplitPane)getContentPane();
			tableContextMenu.show(this, e.getX(), e.getY() + pane.getDividerLocation() + tableContextMenu.getHeight());
		}
	}

	public void mouseReleased(MouseEvent e) {}
}
